#ifndef _imageTools_h_
#define _imageTools_h_

// imageTools.h
//
// Tools for writing generic and specific image processing functions


// Disable the warning regarding long symbol names caused by templates
#ifdef WIN32
#pragma warning(disable:4786)
#endif

#include "image/imageTypes.h"
#include "image/imageTmpl.h"

namespace simple{
    //
    // Image Intersection Routines
    //
    //
    // Image proceessing routines need to operate on
    // intersected regions between source and destination images.
    // These routines handle the calculations

    struct IntersectRects
    {
        Rect src;   // Intersection region for source image(s)
        Rect dst;   // Intersection region for dest image(s)
    };


    IntersectRects intersect (const Rect& src, const Rect& dst);
    IntersectRects intersect (const Rect& src, unsigned int xKernelSize, unsigned int yKernelSize, const Rect& dst);
    IntersectRects intersect (const Rect& src1, const Rect& src2, const Rect& dst);
    // Generic intersection routines. The kernel size must be odd.



    //
    // Function_s1
    //
    // Function object to handle image functions that look like
    //   void function (Image& src)
    //
    //
    // This may seem like a trivial use of this functionality, but it allows
    // the object to validate and lock the image, before an operation is
    // applied. This means all image processing routines that use this class
    // can ignore these issues and only deal with the image processing.
    //
    // The object can be used in one of two ways.
    //   1. You can derive an object from the base class and specify
    //      your own process() method to run on all the pixels
    //   2. You can specify a function pointer to the constructor which
    //      is called once the destination region is determined.

    template<class T1, class S1=ImageStorage<T1> >
    class Function_s1
    {
    public:
        Function_s1 () : _function (0) {}
        // Use this ctor when you override process() yourself

        typedef void(*Function) (Image<T1,S1>& src1);
        Function_s1 (Function f) : _function (f) {}
        // Use this ctor when you specify your process function.

        virtual ~Function_s1 () {};

        void run (Image<T1,S1>& src1) { execute (src1);}
        // Executes the image processing function

    protected:
        Function _function;

        virtual void execute (Image<T1,S1>& src1)
        {
            if (src1.isNull())
                return;

            ImageLocker<T1,S1> srcLocking (src1); // exception-safe locking

            // Run the image processing routine. We need to worry
            // about exceptions because we can be calling user written code
            try {
                process (src1);
            }
            catch (...) {
                // Add any recovery code here.
                throw;
            }

        }

        virtual void process (Image<T1,S1>& src1)
        {
            if (_function)
                _function (src1);
        }
    };



    //
    // Function_s1d1
    //
    // Function object to handle image functions that look like
    //   void function (const Image& src, Image& dst)
    //
    //
    // The function is written this way so a destination can be specified, rather
    // than allocated when the function executes. If a destination is specified,
    // an intersection is performed to determine exactly what pixels will be
    // processed.
    //
    // The object can be used in one of two ways.
    //   1. You can derive an object from the base class and specify
    //      your own process() method to run on all the pixels
    //   2. You can specify a function pointer to the constructor which
    //      is called once the destination region is determined.

    template<class R, class T1, class T2,
             class S1=ImageStorage<T1>, class S2=ImageStorage<T2> >
    class Function_s1d1
    {
    public:
        Function_s1d1 () : _function (0) {}  // Override process() if you are using this

        typedef void(*Function) (const R&, const Image<T1,S1>& src1, Image<T2,S2>& dst1);
        Function_s1d1 (Function f) : _function (f) {}

        virtual ~Function_s1d1 () {};

        void run (const Image<T1,S1>& src1, Image<T2,S2>& dst1)
        { return execute (src1, dst1);}
        // Executes the image processing function

    protected:
        Function     _function; // Our process function, if any
        Image<T1,S1> _roi1;     // roi of src1 image
        Image<T2,S2> _roi2;     // roi of dst1 image

        virtual IntersectRects intersection (const Image<T1,S1>& src1, Image<T2,S2>& dst1)
        { return intersect (src1.boundary(), dst1.boundary());}

        virtual void execute (const Image<T1,S1>& src1, Image<T2,S2>& dst1)
        {
            // If a null image is passed, return a null image
            if (src1.isNull()) {
                dst1 = dst1.sNull;
                return;
            }

            // Exception-safe locking.
            ImageLocker<T1,S1> srcLocking (src1);

            // Create the destination if it was not specified. Create an
            // output image with the same alignment as the input.
            if (dst1.isNull())
                createDestination (src1, dst1);

            // Lock destination after it is created (if necessary)
            ImageLocker<T2,S2> dstLocking (dst1);

            // Compute the overlap between the images
            IntersectRects overlap = intersection (src1, dst1);

            // Return a null image if there is no overlap
            if (overlap.dst.isNull()) {
                dstLocking.unlock(); // We must unlock the object before assignment
                dst1 = dst1.sNull;
                return;
            }

            // Work only within the overlap area
            _roi1 = src1;
            _roi2 = dst1;
            _roi1.window (overlap.src);
            _roi2.window (overlap.dst);

            // Run the image processing routine.
            try {
                process ();
            }
            catch (...) {
                // Add any recovery code here.
                throw;
            }
        }
        // Execute the image processing function on the indicated images

        virtual void createDestination (const Image<T1,S1>& src1, Image<T2,S2>& dst1)
        {
            // Images are already locked as required.
            dst1 = Image<T2,S2> (src1.boundary(), src1.alignment());
        }
        // Create our destination. Derived objects may want to change this

        virtual void process ()
        {
            // placeholder是为了使得编译器能够使用正确的参数来调用_function。
            R placeholder;
            if (_function)
                _function (placeholder, _roi1, _roi2);
        }
    };



    //
    // Function_s1d1Convolve
    //
    // Function object to handle image functions that look like
    //   void function (const Image& src, const char* kernel, unsigned int size, int divisor, Image& dst)
    //
    //
    // The function is written this way so a destination can be specified, rather
    // than allocated when the function executes. If a destination is specified,
    // an intersection is performed to determine exactly what pixels will be
    // processed.
    //
    // The object can be used in one of two ways.
    //   1. You can derive an object from the base class and specify
    //      your own process() method to run on all the pixels
    //   2. You can specify a function pointer to the constructor which
    //      is called once the destination region is determined.

    template<class R, class T1, class T2,
             class S1=ImageStorage<T1>, class S2=ImageStorage<T2> >
    class Function_s1d1Convolve : public Function_s1d1<R,T1,T2,S1,S2>
    {
    public:
        Function_s1d1Convolve () : _function (0) {}
        // Use this ctor when you override process() yourself

        typedef void(*Function) (const R&, const Image<T1,S1>& src1,
                                 const char* kernel, unsigned int size,
                                 int divisor, Image<T2,S2>& dst1);
        Function_s1d1Convolve (Function f) : _function (f) {}
        // Use this ctor when you specify your process function.

        virtual ~Function_s1d1Convolve () {};

        void run (const Image<T1,S1>& src1, 
                  const char* kernel, unsigned int size, int divisor,
                  Image<T2,S2>& dst1)
        {
            _kernel  = kernel;
            _size    = size;
            _divisor = divisor;

            execute (src1, dst1);
        }
        // Executes the image processing function

    protected:
        Function     _function; // Our process function, if any
        const char*  _kernel;   // Pointer to filter kernel (square)
        unsigned int _size;     // Kernel size
        int          _divisor;  // Divisor to apply after filter

        virtual IntersectRects intersection (
            const Image<T1,S1>& src1, Image<T2,S2>& dst1)
        {
            return intersect (src1.boundary(),
                _size, _size, dst1.boundary());
        }

        virtual void createDestination (const Image<T1,S1>& src1, Image<T2,S2>& dst1)
        {
            // Images are already locked as required.

            // Our destination size is smaller than the source
            int expansion = (_size-1) / 2;
            Rect region = src1.boundary ();
            region.expand (-expansion, -expansion);

            dst1 = Image<T2,S2> (region, src1.alignment());
        }
        // Create our destination. 

        virtual void process ()
        {
            R placeholder;
            if (_function)
                _function (placeholder, _roi1, _kernel,
                _size, _divisor, _roi2);
        }
    };

    //
    // Function_s1s2d1
    //
    // Function object to handle image functions that look like
    //   void function (const Image& src1, const Image& src2, Image& dst)
    //
    //
    // The function is written this way so a destination can be specified, rather
    // than allocated when the function executes. If a destination is specified,
    // an intersection is performed to determine exactly what pixels will be
    // processed.
    //
    // The object can be used in one of two ways.
    //   1. You can derive an object from the base class and specify
    //      your own process() method to run on all the pixels
    //   2. You can specify a function pointer to the constructor which
    //      is called once the destination region is determined.

    template<class R, class T1, class T2, class T3,
             class S1=ImageStorage<T1>, class S2=ImageStorage<T2>,
             class S3=ImageStorage<T3> >
    class Function_s1s2d1
    {
    public:
        Function_s1s2d1 () : _function (0) {}
        // Use this ctor when you override process() yourself

        typedef void(*Function) (const R&, const Image<T1,S1>& src1, const Image<T2,S2>& src2, Image<T3,S3>& dst1);
        Function_s1s2d1 (Function f) : _function (f) {}
        // Use this ctor when you specify your process function.

        virtual ~Function_s1s2d1 () {};

        void run (const Image<T1,S1>& src1, const Image<T2,S2>& src2, Image<T3,S3>& dst1)
        { return execute (src1, src2, dst1);}
        // Executes the image processing function

    protected:
        Function     _function; // Our process function, if any
        Image<T1,S1> _roi1;     // roi of src1 image
        Image<T2,S2> _roi2;     // roi of src2 image
        Image<T3,S3> _roi3;     // roi of dst1 image

        virtual IntersectRects intersection (const Image<T1,S1>& src1, const Image<T2,S2>& src2, Image<T3,S3>& dst1)
        { return intersect (src1.boundary(), src2.boundary(), dst1.boundary());}

        virtual void execute (const Image<T1,S1>& src1, const Image<T2,S2>& src2, Image<T3,S3>& dst1)
        {
            // Exception-safe locking.
            ImageLocker<T1,S1> src1Locking (src1);
            ImageLocker<T2,S2> src2Locking (src2);

            Rect srcOverlap = src1.boundary ();
            srcOverlap = srcOverlap.intersect (src2.boundary ());

            // If a null image is passed, return a null image
            if (srcOverlap.isNull()) {
                dst1 = dst1.sNull;
                return;
            }

            // Create the destination if it was not specified. Create an
            // output image with the same alignment as the overlap input.
            if (dst1.isNull()) {
                dst1 = Image<T2,S2> (srcOverl, src1.alignment());
            }

            // Compute the overlap between the images
            IntersectRects overlap = intersection (src1, src2, dst1);

            // Return a null image if there is no overlap
            if (overlap.dst.isNull()) {
                dst1 = dst1.sNull;
                return;
            }

            // Lock destination after it is created (if necessary)
            ImageLocker<T3,S3> dstLocking  (dst1);


            // Work only within the overlap area
            _roi1 = src1;
            _roi2 = src2;
            _roi3 = dst1;
            _roi1.window (overlap.src);
            _roi2.window (overlap.src);
            _roi3.window (overlap.dst);

            // Run the image processing routine.
            try {
                process ();
            }
            catch (...) {
                // Add any recovery code here.
                throw;
            }
        }
        // Execute the image processing function on the indicated images

        virtual void process ()
        {
            R placeholder;
            if (_function)
                _function (placeholder, _roi1, _roi2, _roi3);
        }
    };

}

#endif // _imageTools_h_
